"""
This module is a PyMak extension module.

It defines the rules and actions which enable the creation of scripts
that can be used to run programs written in Python which consist of
many PYTHONPATH and PATH dependencies.

"""

# Python imports
import logging
import os

# PyMak imports
import pymak
from pymak.path import to_path

MAJOR_VERSION = 1
MINOR_VERSION = 0

def init(context):
    """This method sets up into the provided PyMak context.

    The rule is not declared into the supplied context's list of rules
    since there's not much point having a singleton of the rule -
    there's too much instance-specific data needed.

    The action can be decared as a singleton, so the '.pyrunner'
    is bound to a RunScriptAction instance.
     """
    context.declare_action('.pyrunner', RunScriptAction())

def version_ok(major, minor):
    if major != MAJOR_VERSION:
        return False
    if minor > MINOR_VERSION:
        return False
    return True

class RunScriptRule(pymak.Rule):
    def __init__(self, context, module_name, run_function="run", path_extras=[], environ_extras={}, isolate_virtualenv=False):
        super(RunScriptRule, self).__init__(context)
        self.module_name = module_name
        self.run_function = run_function
        self.path_extras = path_extras
        self.environ_extras = environ_extras
        self.isolate_virtualenv = isolate_virtualenv

    def apply_rule(self, target_name, **kwargs):
        variants = kwargs.pop('variants', [])
        variants.append('py')
        attrs = dict(module_name=self.module_name,
                     path_extras=self.path_extras,
                     run_function=self.run_function,
                     environ_extras=self.environ_extras,
                     isolate_virtualenv=self.isolate_virtualenv)
        attrs.update(kwargs.pop('attrs', {}))
        t = self.context.target(target_name,
                                rule=None,
                                variants=variants,
                                force_rebuild=True,
                                target_file=target_name + "-$(allvariants).bat",
                                action=".pyrunner",
                                attrs=attrs,
                                **kwargs)
        return t

class RunScriptAction(pymak.Action):

    def execute_action(self, target, variant_values):
        logging.info("%s...", target.path)
        path_elements = []
        prepend_elements = []
        for t in target._depends_on.list_of_sub_targets(variant_values):
            if getattr(t, 'prepend', False):
                prepend_elements.append(t)
            else:
                path_elements.append(t)
                
        pf = to_path(target.path.namebase + ".py")

        # Write out a python file which sets the PATH, and python-path up appropriately
        py_script_file = pf.open("w")
        py_script_file.write("""import os
import sys
""")
        if target.path_extras:
            py_script_file.write("""
os.environ['PATH'] += os.pathsep + os.pathsep.join([\\
""")
            for p in target.path_extras:
                newpath = pymak.expand_variables(p, variant_values, target._context, target)
                py_script_file.write("    r\"%s\",\n" % newpath)
            py_script_file.write("""    ])
""")

        for k, v in target.environ_extras.items():
            py_script_file.write("""os.environ[%s] = %s
""" % (repr(k), repr(v)))


        py_script_file.write("""sys.path = [
""")
        for t in prepend_elements:
            f = to_path(self.handle_sub_target(target, t, variant_values))
            py_script_file.write("    r\"%s\",\n" % f.abspath())
        if target.isolate_virtualenv:
            # this flag removes all elments from the path that don't start with the sys.prefix. This
            # effectively knocks out all the paths that might have come from the registry which point
            # at the original python installation.
            py_script_file.write("""] + [ p for p in sys.path if p.startswith(sys.prefix) ] + [
""")
        else:
            py_script_file.write("""] + sys.path + [
""")
        for t in path_elements:
            f = to_path(self.handle_sub_target(target, t, variant_values))
            py_script_file.write("    r\"%s\",\n" % f.abspath())

        py_script_file.write("""]
import %s
%s.%s()
""" % (target.module_name, target.module_name, target.run_function))
        py_script_file.close()

        # write out the bat file that can call this python script
        cmd = """@echo off
\"$(PYTHON_HOME)\\python.exe\" \"%s\" %%*
""" % pf
        cmd = pymak.expand_variables(cmd, variant_values, target._context, target)
        target.path.write_text(cmd)


    def handle_sub_target(self, target, t, variant_values):
        if t.path.endswith(".zip"):
            f = t.path
        elif t._is_dir:
            f = t.path
        else:
            f = t.path.parent
        f = pymak.expand_variables(f, variant_values, t._context, t)
        return f
